package com.swipemaster.levels;

import java.util.Timer;
import java.util.TimerTask;

import android.graphics.Canvas;
import android.graphics.Paint;

import com.swipemaster.common.Const;
import com.swipemaster.common.Global;
import com.swipemaster.elements.Circle;
import com.swipemaster.elements.Element;
import com.swipemaster.elements.Particle;
import com.swipemaster.game.StaticClockFace;
import com.swipemaster.moves.FreeMove;

/** Level where player has to stay amount of milliseconds alive. */
public class SurvivalLevel extends Level
{
	/** Timer periodically calls spawner which will produce enemies. */
	protected Timer timer;
	/** Maximum number of spawned enemies. */
	protected int maxSpawns;
	/** Current number of spawns on the screen. */
	private int numberOfSpawns = 0;
	/** Preallocated table of spawned enemies (spawns). */
	private Element spawns[];
	/** Period of spawning in seconds. */
	protected int spawnPeriodSeconds;
	/** Radius of spawns (spawned enemies). */
	protected double spawnsRadius;
	/** Spawns collides with walls or not. */
	protected boolean wallCollision = true;
	/** Enemy spawn points. Universal units. */
	static final double SPAWN_X_UP_LEFT = Const.RADIUS;
	static final double SPAWN_Y_UP_LEFT = Const.RADIUS;
	static final double SPAWN_X_UP_RIGHT = 1 - Const.RADIUS;
	static final double SPAWN_Y_UP_RIGHT = Const.RADIUS;
	static final double SPAWN_POINTS[][] =
	{
	{ SPAWN_X_UP_LEFT, SPAWN_Y_UP_LEFT },
	{ SPAWN_X_UP_RIGHT, SPAWN_Y_UP_RIGHT } };

	public SurvivalLevel(Circle player)
	{
		super(player);
	}

	/** @param player Player circle.
	 * @param timeToWin If 0, this requirement will be ignored.
	 * @param timeFor1Star Time to gain 1 star.
	 * @param timeFor2Stars Time to gain 2 stars.
	 * @param timeFor3Stars Time to gain 3 stars. */
	public SurvivalLevel(Circle player, double timeToWin, double timeFor1Star, double timeFor2Stars,
			double timeFor3Stars)
	{
		super(player, timeToWin, timeFor1Star, timeFor2Stars, timeFor3Stars);
	}

	@Override
	public void draw(Canvas canvas)
	{
		super.draw(canvas);
		if (particlesActive)
		{
			drawParticles(canvas);
		}
		drawTime(canvas);
	}

	public int getSecondsToSpawn()
	{
		return spawnPeriodSeconds;
	}

	@Override
	public int getStars()
	{
		final double playingTime = clock.getPlayingTime();
		if (playingTime > timeFor3Stars)
		{
			return 3;
		}
		if (playingTime > timeFor2Stars)
		{
			return 2;
		}
		if (playingTime > timeFor1Star)
		{
			return 1;
		}
		return 0;
	}

	@Override
	public boolean isLoser()
	{
		return isPlayerCollidingWithEnemy() && (clock.getPlayingTime() < timeToWin);
	}

	@Override
	public boolean isWinner()
	{
		return isPlayerCollidingWithEnemy() && (clock.getPlayingTime() >= timeToWin);
	}

	/** Set periodically spawning of enemies.
	 * @param spawnPeriod How many seconds we have to wait to spawn new enemy.
	 * @param maxSpawns Maximum number of spawned enemies.
	 * @param spawnsRadius Radius of enemies that will be spawned. Universal units. */
	public void setEnemySpawn(int spawnPeriod, int maxSpawns, double spawnsRadius)
	{
		if (spawnPeriod <= 0)
		{
			throw new IllegalArgumentException("Time must be positive");
		}
		if (maxSpawns <= 0)
		{
			throw new IllegalArgumentException("Max numer of spawns must be positive");
		}
		if (spawnsRadius <= 0)
		{
			throw new IllegalArgumentException("Spawns' radius must be positive");
		}
		spawnPeriodSeconds = spawnPeriod;
		this.maxSpawns = maxSpawns;
		this.spawnsRadius = spawnsRadius;
		spawns = new Element[maxSpawns];
	}

	@Override
	public void setRunning(boolean isRunning)
	{
		super.setRunning(isRunning);

		if (isRunning)
		{
			if (spawnPeriodSeconds > 0)
			{
				double timeToNextSpawn = spawnPeriodSeconds - (clock.getPlayingTime() % spawnPeriodSeconds);
				if (!Global.app.isDuringGameplay)
				{
					timeToNextSpawn = 0.0;
				}
				startTimer(timeToNextSpawn);
			}
		}
		else
		{
			killTimer();
		}
	}

	public void setWallCollision(boolean wallCollision)
	{
		this.wallCollision = wallCollision;
	}

	protected void drawParticles(Canvas canvas)
	{
		for (final Particle particle : particles)
		{
			particle.draw(canvas);
		}
	}

	protected void drawTime(Canvas canvas)
	{
		final double playingTime = clock.getPlayingTime();

		Paint paint = Global.app.consts.timeCounterRedPaint;
		if (playingTime >= timeToWin)
		{
			paint = Global.app.consts.timeCounterGreenPaint;
		}

		final char[] text = StaticClockFace.getTimeToDraw(playingTime);
		final int index = StaticClockFace.getStartIndex();
		final int count = StaticClockFace.LENGTH - index;
		canvas.drawText(text, index, count, 5, Global.app.consts.timeCounterTextSize, paint);
	}

	/** Delete all tasks and kill timer thread. */
	private void killTimer()
	{
		if (timer != null)
		{
			timer.cancel();
			timer.purge();
			timer = null;
		}
	}

	private void startTimer(double delay)
	{
		if ((timer == null) && (delay >= 0))
		{
			timer = new Timer();
			timer.purge();
			timer.schedule(new Spawner(spawnsRadius), (long) (1000 * delay), 1000 * spawnPeriodSeconds);
		}
	}

	/** Periodically spawns enemies in separate thread. */
	private class Spawner extends TimerTask
	{
		/** @param radius Radius of enemies that will be spawned. Universal units. */
		public Spawner(double radius)
		{
			for (int i = 0; i < spawns.length; i++)
			{
				double speedX = Const.MIN_SPEED + (Math.random() * (Const.MAX_SPEED - Const.MIN_SPEED));
				if ((i % 2) == 1)
				{
					// Starts from right side, so must have negative x speed.
					speedX = -speedX;
				}
				final double speedY = Const.MIN_SPEED + (Math.random() * (Const.MAX_SPEED - Const.MIN_SPEED));
				final FreeMove freeMove = new FreeMove(speedX, speedY, SurvivalLevel.this);
				freeMove.setParticlesActive(particlesActive);
				freeMove.setWallCollision(wallCollision);
				spawns[i] = new Circle(SPAWN_POINTS[i % 2][0], SPAWN_POINTS[i % 2][1], freeMove, radius, !bounce);
			}
		}

		@Override
		public void run()
		{
			if (numberOfSpawns >= maxSpawns)
			{
				killTimer();
				return;
			}

			addEnemy(spawns[numberOfSpawns]);
			numberOfSpawns++;
		}
	}
}
